/**
 * Created by dada on 17/3/3.
 */

//TODO 兼容性测试

(function (ctx) {
  var ACCORDION_CLS_NAME = 'accordion'

  if (!ctx.$$) {
    ctx.$$ = {}
  }
  var $$ = ctx.$$
  $$.accordion = function (selector, options) {
    if (!selector) {
      selector = '.' + ACCORDION_CLS_NAME
    }

    var nodes = document.querySelectorAll(selector)
    var collection = []

    // 每个元素创建一个Accordion实例
    for (var i = 0; i < nodes.length; i++) {
      collection.push(new Accordion(nodes[i], options))
    }

    // 返回的是集合, open, close, toggle方法默认操作集合的第一个实例
    var methods = ['open', 'close', 'toggle']
    for (var i = 0; i < methods.length; i++) {
      _bindMethod(collection, collection[0], methods[i])
    }

    collection.get = function (idx) {
      return collection[idx]
    }

    // destroy方法销毁所有实例
    collection.destroy = function () {
      for (var i = 0; i < collection.length; i++) {
        (collection[i]).destroy.call(collection[i])
      }
      return collection
    }

    return collection
  }

  function _bindMethod(collection, accordion, method) {
    collection[method] = function () {
      console.log('call: ', method)
      if (accordion) {
        accordion[method].apply(accordion, arguments)
      }
      return collection
    }
  }

  /**
   * 构造函数
   * @param $el 节点元素
   * @options options 配置选项
   * @returns {实例}
   * @constructor
   */
  function Accordion($el, options) {
    // 初始化
    return _init.call(this, $el, options)
  }

  /**
   * 初始化实例, 私有方法
   * @param $el 节点元素
   * @options options 配置选项
   * @returns {实例}
   * @private
   */
  function _init($el, options) {
    // 防止重复初始化
    if ($el.__accordion) {
      console.warn('accordion already init: ', $el)
      return $el.__accordion
    }

    // 默认配置
    this.__options = {
      active: 0,
      animation: 300
    }
    // 用户自定义配置
    for (var x in options) {
      if (options.hasOwnProperty(x) && this.__options.hasOwnProperty(x)) {
        this.__options[x] = options[x]
      }
    }

    // 添加Accordion样式
    if (!hasClass($el, ACCORDION_CLS_NAME)) {
      addClass($el, ACCORDION_CLS_NAME)
    }

    _initHeaders.call(this, $el)
    _initPanels.call(this, $el)
    _initEvents.call(this)

    this.$el = $el
    $el.__accordion = this;

    // 打开默认panel
    var active = this.__options.active
    this.open(this.__panels[active] && active || 0)

    return this
  }

  /**
   * 初始化headers
   * @private
   */
  function _initHeaders($el) {
    this.__headers = $el.querySelectorAll(':scope > h3')
  }

  /**
   * 初始化panels
   * @private
   */
  function _initPanels($el) {
    var panels = this.__panels = $el.querySelectorAll(':scope > h3 + div')
    for (var i = 0; i < panels.length; i++) {
      panels[i].setAttribute('data-height', _getComputedStyle(panels[i]).height)
      panels[i].style.height = '0px'
      _setPanelTransition.call(this, panels[i], this.__options.animation)
    }
  }

  /**
   * 初始化事件绑定
   * @private
   */
  function _initEvents() {
    var headers = this.__headers
    this.__handlers = []
    for (var i = 0; i < headers.length; i++) {
      _bindClick.call(this, headers[i], i, this.__handlers)
    }
  }

  /**
   * 绑定header点击事件
   * @param header
   * @param idx
   * @param handlers
   * @private
   */
  function _bindClick(header, idx, handlers) {
    var that = this
    var handler = function (e) {
      that.toggle(idx)
    }
    header.addEventListener('click', handler)
    handlers.push({
      node: header,
      name: 'click',
      handler: handler
    })
  }

  /**
   * 获取元素计算后样式样式
   * @param elem
   * @returns {*}
   * @private
   */
  function _getComputedStyle(elem) {
    if (window.getComputedStyle) {
      return window.getComputedStyle(elem)
    } else if (elem.currentStyle) {
      return elem.currentStyle
    }
    return {}
  }

  function _getNumber(num, defaultValue, min) {
    if (isNaN(Number(num))) {
      num = defaultValue
    } else if (min !== undefined && num < min) {
      num = defaultValue
    }
    return num
  }

  /**
   * 设置panel过渡动画
   * @param panel
   * @param time
   * @private
     */
  function _setPanelTransition(panel, time) {
    time = _getNumber(time, 300, 0)
    if (time) {
      panel.style.transition = 'height ' + time + 'ms ease'
    } else {
      panel.style.transition = 'none'
    }
  }

  /**
   * 展开panel
   * @param panel
   * @private
   */
  function _open(panel) {
    if (!panel) {
      return
    }
    console.log('open: ', panel)
    panel.style.height = panel.getAttribute('data-height')
  }

  /**
   * 收起panel
   * @param panel
   * @private
   */
  function _close(panel) {
    if (!panel) {
      return
    }
    console.log('close: ', panel)
    panel.style.height = '0px'
  }

  /**
   * 展开或收起panel
   * @param panel
   * @private
   */
  function _toggle(panel) {
    if (!panel) {
      return
    }
    if (_isOpen(panel)) {
      _close(panel)
    } else {
      _open(panel)
    }
  }

  /**
   * 判断panel是否处于展开状态
   * @param panel
   * @returns {boolean}
   * @private
   */
  function _isOpen(panel) {
    return panel.style.height != '0px'
  }

  /**
   * 展开或收起Accordion的第idx个panel
   * @param idx 序号, 从0开始
   * @returns {Accordion}
   */
  Accordion.prototype.toggle = function (idx) {
    console.log('toggle: ', idx)
    if (!this.__panels) {
      console.warn('accordion already destroy')
      return this
    }
    if (!this.__panels[idx]) {
      console.warn('accordion panel not found')
      return this
    }
    var panels = this.__panels
    var panel
    for (var i = 0; i < panels.length; i++) {
      panel = this.__panels[i]
      if (i == idx) {
        _toggle(panel)
      } else {
        _close(panel)
      }
    }
    return this
  }

  /**
   * 展开Accordion的第idx个panel
   * @param idx 序号, 从0开始
   * @returns {Accordion}
   */
  Accordion.prototype.open = function (idx) {
    console.log('open: ', idx)
    if (!this.__panels) {
      console.warn('accordion already destroy')
      return this
    }
    if (!this.__panels[idx]) {
      console.warn('accordion panel not found')
      return this
    }
    var panels = this.__panels
    var panel
    for (var i = 0; i < panels.length; i++) {
      panel = panels[i]
      if (i == idx) {
        _open(panel)
      } else {
        _close(panel)
      }
    }
    return this
  }

  /**
   * 收起Accordion的第idx个panel
   * @param idx 序号, 从0开始
   * @returns {Accordion}
   */
  Accordion.prototype.close = function (idx) {
    console.log('close: ', idx)
    if (!this.__panels) {
      console.warn('accordion already destroy')
      return this
    }
    if (!this.__panels[idx]) {
      console.warn('accordion panel not found')
      return this
    }
    _close(this.__panels[idx])
    return this
  }

  /**
   * 返回Accordion的节点元素
   * @returns {*}
   */
  Accordion.prototype.getNode = function () {
    return this.$el
  }

  /**
   * 销毁当前Accordion实例
   * @returns {Accordion}
     */
  Accordion.prototype.destroy = function () {
    if (this.$el) {
      console.log('destroy accordion')
      var handlers = this.__handlers
      var handler
      for (var i = 0; i < handlers.length; i++) {
        handler = handlers[i]
        handler.node.removeEventListener(handler.name, handler.handler)
      }
      var panels = this.__panels
      var panel
      for (var i = 0; i < this.__panels.length; i++) {
        panel = panels[i]
        panel.style.height = panel.getAttribute('data-height')
        panel.style.transition = 'none'
      }
      delete this.$el.__accordion
      delete this.$el
      delete this.__handlers
      delete this.__headers
      delete this.__panels
    }
    return this
  }


  function hasClass(el, cls) {
    cls = cls || ''
    if (cls.replace(/\s/g, '').length == 0) {
      return false
    }
    return new RegExp(' ' + cls + ' ').test(' ' + el.className + ' ')
  }

  function addClass(el, cls) {
    if (!hasClass(el, cls)) {
      el.className = el.className == '' ? cls : el.className + ' ' + cls
    }
  }

  function removeClass(el, cls) {
    if (hasClass(el, cls)) {
      var newClass = ' ' + el.className.replace(/[\t\r\n]/g, '') + ' '
      while (newClass.indexOf(' ' + cls + ' ') >= 0) {
        newClass = newClass.replace(' ' + cls + ' ', ' ');
      }
      el.className = newClass.replace(/^\s+|\s+$/g, '');
    }
  }
})(window)
